use std::env;
use std::fs::File;
use std::io::{self, BufRead, BufReader};

pub fn grep()-> io::Result<()> {
    /*
      获取命令行参数，并把参数转换成Vec<String>类型。env::args()返回一个迭代器，collect方法可以把迭代器收集到集合中。
      let args = env::args().collect::<Vec<_>>();
      let args = env::args().collect::<Vec<String>>();
      let args: Vec<String> = env::args().collect();
      let args: Vec<_> = env::args().collect();

      Rust的类型推断是非常强大的，如果后面对args的使用能确定args的类型的话，也可以完全写成：
      let args = env::args().collect();

      PS:使用let定义变量，默认为不可变类型。如果要定义可变类型，需要加上mut关键字。
      let mut args = env::args().collect();

      Vec是集合类型，因为rust编译器已经预先包括了部分std中的模块，所以可以直接使用Vec，而不用使用use引入。
    */
    let args = env::args().collect::<Vec<_>>();
    if args.len() < 3 {
        // 使用eprintln!打印错误信息，并退出程序。std::process::exit(1)表示以状态码1退出程序。
        eprintln!("grep退出任务：Usage: greplite <search_string> <file_path>");
        // 退出程序，状态码为1。因为demo代码需要继续往下执行，所以就先不退出进程了
        // std::process::exit(1);
        // 临时返回错误信息
        return Err(io::Error::new(io::ErrorKind::Other, "Invalid arguments"));
    }

    // 获得集合中的1和2的引用。
    let search_string = &args[1];
    let file_path = &args[2];

    // 调用run函数，并把search_string和file_path作为参数传递。
    run(search_string, file_path)
}

fn run(search_string: &str, file_path: &str) -> io::Result<()> {
    /*
      打开文件，并把结果赋值给file变量。File::open是标准库中的一个函数，用来打开一个文件。
      使用?语法糖。相当于以下语句，成功返回文件，失败返回错误。
          let file = match File::open(file_path) {
              Ok(f) => f,
              Err(err) => return Err(From::from(err)),
          };
    */
    let file = File::open(file_path)?;
    // file可以实现基础的文件操作，而BufReader是对file的封装，提供了更高级的文件操作，使用更加便捷。
    let reader = BufReader::new(file);
    // 使用迭代器获得每一行内容。
    for line in reader.lines() {
        // 使用的迭代器获得的line是一个Result：type Item = Result<String, Error>
        // 通过 ? 语法糖，如果line是Ok，则返回里面的String；如果是Err，则直接返回错误。
        let line = line?;
        if line.contains(search_string) {
            // 打印包含搜索字符串的行。
            // !是一个宏。因为rust中函数不支持可变参数，通过宏将实现可变参数。
            println!("{line}");
        }
    }
    // 最后一个默认为返回值。不需要写return。返回Ok(())，表示函数执行成功。()是一个元组，可以包含多个返回值，这里没有返回值，所以使用空元组。
    Ok(())
}